using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using MyAI.NeuralNetworkLib.Specification;
using MyAI.NeuralNetworkLib.Implementation;

namespace MyAI.NeuralNetworkLib.Utils
{
    public class NNPacker
    {
        /// <summary>
        /// Only for testing, restore simple neuron, simple layer, BackProp_LogicSigmoid
        /// </summary>
        public static MultiLayerFeedForwardNeuralNetwork RestoreMLFFNNFromXmlDocument(XmlDocument xDoc)
        {
            MultiLayerFeedForwardNeuralNetwork nn = new MultiLayerFeedForwardNeuralNetwork();
            List<List<List<double>>> fullStructure = new List<List<List<double>>>();

            XmlNode xRoot = xDoc.DocumentElement;

            ActivationFunctionTypes afType = ActivationFunctionTypes.LogisticSigmoid;
            List<double> afParams = new List<double>();

            XmlNode xOptions = xRoot.ChildNodes.Item(0);
            foreach (XmlNode xNode in xOptions.ChildNodes)
            {
                if (xNode.Name == "ActivationFunction")
                {
                    foreach (XmlAttribute xAttr in xNode.Attributes)
                    {
                        if (xAttr.Name == "Type")
                        {
                            switch (xAttr.Value)
                            { 
                                case "LogisticSigmoid":
                                    afType = ActivationFunctionTypes.LogisticSigmoid;
                                    break;
                            }
                        }
                    }
                    foreach (XmlNode xAFParam in xNode.ChildNodes)
                    {
                        afParams.Add(Convert.ToDouble(xAFParam.InnerText));
                    }
                }
            }



            foreach (XmlNode xLayer in xRoot.ChildNodes.Item(1).ChildNodes)
            {
                List<List<double>> layer = new List<List<double>>();
                foreach (XmlNode xNeuron in xLayer.ChildNodes)
                {
                    List<double> neuron = new List<double>();
                    foreach (XmlNode xWeight in xNeuron.ChildNodes)
                    {
                        neuron.Add(Convert.ToDouble(xWeight.InnerText));
                    }
                    layer.Add(neuron);
                }
                fullStructure.Add(layer);
            }


            List<int> structure = new List<int>();
            foreach (List<List<double>> layer in fullStructure)
            {
                structure.Add(layer.Count);
            }

            nn.GenerateNeuralNetwork(fullStructure[0][0].Count, structure, NeuralLayerTypes.SimpleLayer,
                NeuronTypes.SimpleNeuron, afType, afParams, LearningMethods.BackProp_LogicSigmoid);


            for (int i = 0; i < nn.Layers.Count; i++)
            {
                for (int j = 0; j < nn.Layers[i].GetNeurons().Count; j++)
                {
                    for (int k = 0; k < nn.Layers[i].GetNeuron(j).Weights.Count; k++)
                    { 
                        nn.Layers[i].GetNeuron(j).SetWeight(k, fullStructure[i][j][k]);
                    }
                }
            }

            return nn;
        }

        /// <summary>
        /// Only for testing, restore simple neuron, simple layer, BackProp_LogicSigmoid
        /// </summary>
        public static XmlDocument MLFFNNToXmlDocument(IMultiLayerFeedForwardNeuralNetwork nn)
        {
            XmlDocument xDoc = new XmlDocument();
            XmlNode xLayer = null;
            XmlNode xNeuron = null;
            XmlNode xWeight = null;
            xDoc.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root></root>");
            XmlNode xRoot = xDoc.DocumentElement;

            //Options of the NN
            XmlNode xOptions = xDoc.CreateNode(XmlNodeType.Element, "Options", null);

            //Activation function
            XmlNode xAF = xDoc.CreateNode(XmlNodeType.Element, "ActivationFunction", null);

            XmlAttribute xAFType = xDoc.CreateAttribute("Type");
            xAFType.Value = nn.AFType.ToString();
            xAF.Attributes.Append(xAFType);

            int pNum = 0;
            foreach (double val in nn.AFParams)
            {
                pNum++;
                XmlNode xAfParam = xDoc.CreateNode(XmlNodeType.Element, "Param", null);
                xAfParam.InnerText = val.ToString();
                XmlAttribute xAfParamAttr = xDoc.CreateAttribute("id");
                xAfParamAttr.Value = pNum.ToString();
                xAfParam.Attributes.Append(xAfParamAttr);
                xAF.AppendChild(xAfParam);
            }
            
            xOptions.AppendChild(xAF);

            xRoot.AppendChild(xOptions);

            //Structure and values of weights
            XmlNode xStructure = xDoc.CreateNode(XmlNodeType.Element, "Structure", null);
            int lNum = 0;
            foreach (INeuralLayer layer in nn.Layers)
            { 
                lNum++;
                xLayer = xDoc.CreateNode(XmlNodeType.Element, "Layer", null);
                XmlAttribute xLayerAttr = xDoc.CreateAttribute("id");
                xLayerAttr.Value = lNum.ToString();
                xLayer.Attributes.Append(xLayerAttr);
                List<INeuron> neurons = layer.GetNeurons();
                int nNum = 0;
                foreach (INeuron neuron in neurons)
                {
                    nNum++;
                    xNeuron = xDoc.CreateNode(XmlNodeType.Element, "Neuron", null);
                    int wNum = 0;
                    XmlAttribute xNeuronAttr = xDoc.CreateAttribute("id");
                    xNeuronAttr.Value = nNum.ToString();
                    xNeuron.Attributes.Append(xNeuronAttr);
                    foreach (double val in neuron.Weights)
                    {
                        wNum++;
                        xWeight = xDoc.CreateNode(XmlNodeType.Element, "Weight", null);
                        xWeight.InnerText = val.ToString();
                        XmlAttribute xWeightAttr = xDoc.CreateAttribute("id");
                        xWeightAttr.Value = wNum.ToString();
                        xWeight.Attributes.Append(xWeightAttr);
                        xNeuron.AppendChild(xWeight);
                    }
                    xLayer.AppendChild(xNeuron);
                }
                xStructure.AppendChild(xLayer);
            }
            xRoot.AppendChild(xStructure);

            return xDoc;
        }
    }
}
